﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using WebChatApi.Common.Interface;

namespace WebChatApi.Common.Audit
{
    /// <summary>
    /// Class for audit item. It can be involved in <see cref="Changeset"/> objects. Each <see cref="AuditItem"/> maps to one entity in one table.
    /// </summary>
    [DataContract]
    public class AuditItem : IAuditable
    {
        #region Properties

        /// <summary>
        /// Gets or sets the key.
        /// </summary>
        /// <value>
        /// The key.
        /// </value>
        [DataMember]
        public Guid Key
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the name of the source.
        /// NOTE: This property is commonly used to save database table name
        /// </summary>
        /// <value>
        /// The name of the source.
        /// </value>
        [DataMember]
        public string SourceName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the source identity.
        /// NOTE: This property is commonly used to save database id/guid.
        /// </summary>
        /// <value>
        /// The source identity.
        /// </value>
        [DataMember]
        public string SourceIdentity
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the data XML.
        /// </summary>
        /// <value>
        /// The data XML.
        /// </value>
        [DataMember]
        public XElement DataXml
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the changeset id.
        /// </summary>
        /// <value>
        /// The changeset id.
        /// </value>
        [DataMember]
        public long ChangesetId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the created stamp.
        /// </summary>
        /// <value>
        /// The created stamp.
        /// </value>
        [DataMember]
        public DateTime CreatedStamp
        {
            get;
            set;
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="AuditItem"/> class.
        /// </summary>
        public AuditItem()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AuditItem"/> class.
        /// </summary>
        /// <param name="sourceName">Name of the source.</param>
        /// <param name="sourceIdentity">The source identity.</param>
        /// <param name="dataXml">The data XML.</param>
        public AuditItem(string sourceName, string sourceIdentity, XElement dataXml)
        {
            this.SourceName = sourceName;
            this.SourceIdentity = sourceIdentity;
            this.DataXml = dataXml;
        }

        #endregion

        /// <summary>
        /// To the XML.
        /// </summary>
        /// <returns></returns>
        public XElement ToXml()
        {
            return GenerateAuditItemXml(this);
        }

        /// <summary>
        /// To the changeset.
        /// </summary>
        /// <param name="operatorKey">The operator key.</param>
        /// <param name="comments">The comments.</param>
        /// <returns><see cref="Changeset"/> instance object which contains only one <see cref="AuditItem"/> for itself.</returns>
        public Changeset ToChangeset(Guid operatorKey, string comments)
        {
            return ToChangeset(this, operatorKey, comments);
        }


        /// <summary>
        /// Generates the pure object XML.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns><see cref="XElement"/> instance for xml.</returns>
        public static XElement GeneratePureObjectXml(object obj)
        {
            StringBuilder stringBuilder = new StringBuilder();

            XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
            //Add an empty namespace and empty value
            ns.Add(string.Empty, string.Empty);
            XmlWriterSettings settings = new XmlWriterSettings();

            settings.OmitXmlDeclaration = true; // Remove the <?xml version="1.0" encoding="utf-8"?>

            XmlWriter writer = XmlWriter.Create(stringBuilder, settings);
            //Create the serializer
            XmlSerializer slz = new XmlSerializer(obj.GetType());

            //Serialize the object with our own namespaces (notice the overload)
            slz.Serialize(writer, obj, ns);
            writer.Flush();

            return XElement.Parse(stringBuilder.ToString());
        }

        /// <summary>
        /// Generates the audit item XML.
        /// </summary>
        /// <param name="auditItem">The audit item.</param>
        /// <returns></returns>
        protected static XElement GenerateAuditItemXml(AuditItem auditItem)
        {
            XElement result = XElement.Parse("<AuditItem></AuditItem>");
            result.SetAttributeValue("SourceName", auditItem.SourceName);
            result.SetAttributeValue("SourceIdentity", auditItem.SourceIdentity);
            XElement dataXml = XElement.Parse("<DataXml></DataXml>");

            if (auditItem != null && auditItem.DataXml != null)
            {
                dataXml.Add(auditItem.DataXml);
            }

            result.Add(dataXml);

            return result;
        }

        /// <summary>
        /// To the changeset.
        /// </summary>
        /// <param name="thisValue">The this value.</param>
        /// <param name="operatorKey">The operator key.</param>
        /// <param name="comments">The comments.</param>
        /// <returns><see cref="Changeset"/> instance object which contains only one <see cref="AuditItem"/> for itself.</returns>
        public static Changeset ToChangeset(AuditItem thisValue, Guid operatorKey, string comments)
        {
            Changeset result = new Changeset();
            if (thisValue != null)
            {
                result.OperatorKey = operatorKey;
                result.Comments = comments;
                result.AuditItems.Add(thisValue);
            }
            return result;
        }


    }
}
